package com.zusmart.core.proxy.support;

import java.lang.reflect.Method;

import com.zusmart.base.util.Assert;
import com.zusmart.core.proxy.ProxyChain;
import com.zusmart.core.proxy.ProxyHandler;
import com.zusmart.core.proxy.ProxyMethod;

public abstract class AbstractProxyChain implements ProxyChain {

	protected final Class<?> targetClass;
	protected final Object targetInstance;
	protected final Method targetMethod;
	protected final Object[] targetArguments;
	protected final ProxyMethod targetProxyMethod;
	protected final ProxyHandler[] targetProxyHandlers;

	public AbstractProxyChain(Class<?> targetClass, Object targetInstance, Object[] targetArguments, ProxyMethod targetProxyMethod, ProxyHandler[] targetProxyHandlers) {
		Assert.isNull(targetClass, "target class must not be null");
		Assert.isNull(targetInstance, "target instance must not be null");
		Assert.isNull(targetProxyMethod, "target proxy method must not be null");
		Assert.isNull(targetProxyMethod.getTargetMethod(), "target method must not be null");
		Assert.isTrue(null == targetProxyHandlers || targetProxyHandlers.length == 0, "target proxy handler must not be null");
		this.targetClass = targetClass;
		this.targetInstance = targetInstance;
		this.targetMethod = targetProxyMethod.getTargetMethod();
		this.targetArguments = targetArguments;
		this.targetProxyMethod = targetProxyMethod;
		this.targetProxyHandlers = targetProxyHandlers;
	}

	@Override
	public Class<?> getTargetClass() {
		return this.targetClass;
	}

	@Override
	public Object getTargetInstance() {
		return this.targetInstance;
	}

	@Override
	public Method getTargetMethod() {
		return this.targetMethod;
	}

	@Override
	public Object[] getTargetArguments() {
		return this.targetArguments;
	}

	@Override
	public ProxyMethod getTargetProxyMethod() {
		return this.targetProxyMethod;
	}

	@Override
	public ProxyHandler[] getTargetProxyHandlers() {
		return this.targetProxyHandlers;
	}

	@Override
	public Object doProxyChain() throws Throwable {
		if (this.targetMethod.getDeclaringClass().equals(Object.class)) {
			return this.targetProxyMethod.invokeTargetMethod(this.targetInstance, this.targetArguments);
		}
		return this.doProxyChain(this.targetInstance, this.targetProxyMethod);
	}

	protected abstract Object doProxyChain(Object targetInstance, ProxyMethod proxyMethod) throws Throwable;

}